home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Libraries / hash / md5drvr.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-25  |  20.3 KB  |  803 lines  |  [TEXT/R*ch]

  1. /* %W% %G% %U% */
  2. /*
  3.  * md5drvr - md5 driver code
  4.  *
  5.  * Written by Landon Curt Noll  (chongo@toad.com)
  6.  *
  7.  * This file was written by:
  8.  *
  9.  *     Landon Curt Noll  (chongo@toad.com)    chongo <was here> /\../\
  10.  *
  11.  * This code has been placed in the public domain.  Please do not
  12.  * copyright this code.
  13.  *
  14.  * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH  REGARD  TO
  15.  * THIS  SOFTWARE,  INCLUDING  ALL IMPLIED WARRANTIES OF MER-
  16.  * CHANTABILITY AND FITNESS.  IN NO EVENT SHALL  LANDON  CURT
  17.  * NOLL  BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
  18.  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM  LOSS  OF
  19.  * USE,  DATA  OR  PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  20.  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR  IN
  21.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  22.  *
  23.  ***
  24.  *
  25.  * NOTE: The version information below refers to all md5 code, not
  26.  *     just this file.  In particular, this file was created by
  27.  *     Landon Curt Noll.
  28.  *
  29.  * Version 1.1: 17 Feb 1990        RLR
  30.  *    Original code written.
  31.  *
  32.  * Version 1.2: 27 Dec 1990        SRD,AJ,BSK,JT
  33.  *    C reference version.
  34.  *
  35.  * Version 1.3: 27 Apr 1991        RLR
  36.  *    G modified to have y&~z instead of y&z
  37.  *    FF, GG, HH modified to add in last register done
  38.  *    access pattern: round 2 works mod 5, round 3 works mod 3
  39.  *    distinct additive constant for each step
  40.  *    round 4 added, working mod 7
  41.  *
  42.  * Version 1.4: 10 Jul 1991        SRD,AJ,BSK,JT
  43.  *    Constant correction.
  44.  *
  45.  * Version 2.1:    31 Dec 1993        Landon Curt Noll   (chongo@toad.com)
  46.  *     Modified/Re-wrote md5.c
  47.  *
  48.  * Version 2.2: 09 Jan 1994        Landon Curt Noll   (chongo@toad.com)
  49.  *     md5drvr.c and md5dual.c code cloned from shs version 2.5.8 94/01/09
  50.  *     performance tuning
  51.  *
  52.  * Version 2.3: 10 Jan 1994        Landon Curt Noll   (chongo@toad.com)
  53.  *     added MUST_ALIGN for Sparc and other RISC architectures
  54.  *     use must_align.c to automatically determine if MUST_ALIGN is required
  55.  *     performance tuning
  56.  *     increased test to 64 megabytes due to increased performance
  57.  *
  58.  * Version 2.4:    non-existent version
  59.  *
  60.  * Version 2.5:    non-existent version
  61.  *
  62.  * Version 2.6: 10 Jan 1994        Landon Curt Noll   (chongo@toad.com)
  63.  *     Merged the shs and md5 Makefiles to build both in the same directory
  64.  *     Bumped version to 2.6 to match level to shs
  65.  *     Test suite header now says md5 (not MD5)
  66.  *     Minor performance improvements
  67.  *
  68.  * Version 2.7: 14 Jan 1994        Landon Curt Noll   (chongo@toad.com)
  69.  *     code cleanup
  70.  *     chunk is now 64 bytes, block is determined by blocking factor
  71.  *     magic 64 and 64 related values defined in terms of #defines
  72.  *     fixed bit count carry bug
  73.  *     fixed writable strings test bug
  74.  *
  75.  * Version 2.8: 22 Jan 1994        Landon Curt Noll   (chongo@toad.com)
  76.  *     code cleanup
  77.  *     count bytes in driver, convert to 64 bit count in final transform
  78.  *     handle read errors and EOF better
  79.  *     prefix strings not multiple of 64 bytes in length do not slow down hash
  80.  *     renumbered exit codes
  81.  *     fixed dual digest split bug
  82.  *
  83.  * Version 2.9: 05 Feb 1994        Landon Curt Noll   (chongo@toad.com)
  84.  *     prep for beta release
  85.  *
  86.  * Version 2.10: 24 Mar 1994        Landon Curt Noll   (chongo@toad.com)
  87.  *     must_align catchs signal to detect misaligned access
  88.  *     malloc type not declared
  89.  *
  90.  * Version 2.11: 25 mar 1994        Landon Curt Noll   (chongo@toad.com)
  91.  *     added CERT md5 compatibility
  92.  */
  93.  
  94. #include <stdio.h>
  95. #include <stdlib.h>
  96. #include <string.h>
  97. #include <time.h>
  98. #include <sys/time.h>
  99. #include <sys/resource.h>
  100. #include "md5.h"
  101.  
  102. /* size of test in megabytes */
  103. #define TEST_MEG 64
  104.  
  105. /* number of chunks to process */
  106. #define TEST_CHUNKS (TEST_MEG*1024*1024/READSIZE)
  107.  
  108. /* MD5 test suite strings */
  109. #define ENTRY(str) {(BYTE *)str, NULL, sizeof(str)-1}
  110. struct MD5_test {
  111.     BYTE *ro_data;    /* read only string data or NULL to test */
  112.     BYTE *data;        /* data or NULL to test */
  113.     int len;        /* length of data */
  114. } MD5_test_data[] = {
  115.     ENTRY(""),
  116.     ENTRY("a"),
  117.     ENTRY("abc"),
  118.     ENTRY("message digest"),
  119.     ENTRY("abcdefghijklmnopqrstuvwxyz"),
  120.     ENTRY("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"),
  121.     ENTRY("12345678901234567890123456789012345678901234567890123456789012345678901234567890")
  122. };
  123. #define MAX_MD5_TEST_DATA (sizeof(MD5_test_data)/sizeof(MD5_test_data[0]))
  124.  
  125. /* MD5 test filenames */
  126. char *MD5_test_file[] = {
  127.     "file1",
  128.     "file2",
  129. };
  130. #define MAX_MD5_TEST_FILE (sizeof(MD5_test_file)/sizeof(MD5_test_file[0]))
  131.  
  132. /* where the test files are located by default */
  133. #if !defined(TLIB)
  134. #define TLIB "."
  135. #endif
  136.  
  137. /* Prototypes of the static functions */
  138. static void MD5Stream P((BYTE*, UINT, FILE*, MD5_CTX*));
  139. static void MD5File P((BYTE*, UINT, char*, MD5_CTX*));
  140. static void MD5Output P((char*, int, MD5_CTX*));
  141. static int MD5PreFileRead P((char*, BYTE**));
  142. static void MD5TestSuite P((void));
  143. static void MD5Help P((void));
  144. void main P((int, char**));
  145.  
  146. /* global variables */
  147. char *program;            /* our name */
  148. static int c_flag = 0;        /* 1 => print C style digest with leading 0x */
  149. int i_flag = 0;            /* 1 => process inode & filename */
  150. int q_flag = 0;            /* 1 => print the digest only */
  151. int dot_zero = 0;        /* 1 => print .0 after the digest */
  152. ULONG zero[MAXBLOCK/sizeof(ULONG)];    /* block of zeros */
  153.  
  154.  
  155. /*
  156.  * MD5Stream - digest a open file stream
  157.  */
  158. static void
  159. MD5Stream(pre_str, pre_len, stream, dig)
  160.     BYTE *pre_str;        /* data prefix or NULL */
  161.     UINT pre_len;        /* length of pre_str */
  162.     FILE *stream;        /* the stream to process */
  163.     MD5_CTX *dig;        /* current digest */
  164. {
  165.     ULONG data[READWORDS];    /* our read buffer */
  166.     int bytes;            /* bytes last read */
  167.     int ret;            /* partial fread return value */
  168.  
  169.     /*
  170.      * pre-process prefix if needed
  171.      */
  172.     if (pre_str != NULL) {
  173.     MD5Update(dig, pre_str, pre_len);
  174.     COUNT(dig, pre_len);
  175.     }
  176.  
  177.     /*
  178.      * if we have a partial chunk, try to read until we have a full chunk
  179.      */
  180.     clearerr(stream);
  181.     if (dig->datalen > 0) {
  182.  
  183.         /* determine what we have so far */
  184.         bytes = dig->datalen;
  185.  
  186.     /* try to read what we need to fill the chunk */
  187.     while (bytes < MD5_CHUNKSIZE) {
  188.  
  189.         /* try to read what we need */
  190.         ret = fread((char*)data+bytes, 1, MD5_CHUNKSIZE-bytes, stream);
  191.  
  192.         /* carefully examine the result */
  193.         if (ret < 0 || ferror(stream)) {
  194.         /* error processing */
  195.         fprintf(stderr, "%s: ", program);
  196.         perror("read #3 error");
  197.         exit(1);
  198.         } else if (ret == 0 || feof(stream)) {
  199.         /* EOF processing */
  200.         COUNT(dig, MD5_CHUNKSIZE-dig->datalen);
  201.         MD5Update(dig, (BYTE *)data+dig->datalen, 
  202.           MD5_CHUNKSIZE-dig->datalen);
  203.         return;
  204.         }
  205.  
  206.         /* note that we have more bytes */
  207.         bytes += ret;
  208.         }
  209.         COUNT(dig, MD5_CHUNKSIZE-dig->datalen);
  210.         MD5Update(dig, (BYTE *)data+dig->datalen, MD5_CHUNKSIZE-dig->datalen);
  211.     }
  212.  
  213.     /*
  214.      * process the contents of the file
  215.      */
  216.     while ((bytes = fread((char *)data, 1, READSIZE, stream)) > 0) {
  217.  
  218.     /*
  219.      * if we got a partial read, try to read up to a full chunk
  220.      */
  221.     while (bytes < READSIZE) {
  222.  
  223.         /* try to read more */
  224.         ret = fread((char *)data+bytes, 1, READSIZE-bytes, stream);
  225.  
  226.         /* carefully examine the result */
  227.         if (ret < 0 || ferror(stream)) {
  228.             /* error processing */
  229.             fprintf(stderr, "%s: ", program);
  230.             perror("read #1 error");
  231.             exit(2);
  232.         } else if (ret == 0 || feof(stream)) {
  233.             /* EOF processing */
  234.             MD5Update(dig, (BYTE *)data, bytes);
  235.             COUNT(dig, bytes);
  236.             return;
  237.         }
  238.  
  239.         /* note that we have more bytes */
  240.         bytes += ret;
  241.     }
  242.  
  243.     /*
  244.      * digest the read
  245.      */
  246.     MD5fullUpdate(dig, (BYTE *)data, bytes);
  247.     COUNT(dig, bytes);
  248.     }
  249.  
  250.     /*
  251.      * watch for errors
  252.      */
  253.     if (bytes < 0 || ferror(stream)) {
  254.     /* error processing */
  255.     fprintf(stderr, "%s: ", program);
  256.     perror("read #2 error");
  257.     exit(3);
  258.     }
  259.     return;
  260. }
  261.  
  262.  
  263. /*
  264.  * MD5File - digest a file
  265.  */
  266. static void
  267. MD5File(pre_str, pre_len, filename, dig)
  268.     BYTE *pre_str;        /* string prefix or NULL */
  269.     UINT pre_len;        /* length of pre_str */
  270.     char *filename;        /* the filename to process */
  271.     MD5_CTX *dig;        /* current digest */
  272. {
  273.     FILE *inFile;        /* the open file stream */
  274.     struct stat buf;        /* stat or lstat of file */
  275.     struct hashstat hashbuf;    /* stat data to digest */
  276.     struct hashstat hashlbuf;    /* lstat data to digest */
  277.     ULONG filename_len;        /* length of the filename */
  278.  
  279.     /*
  280.      * open the file
  281.      */
  282.     inFile = fopen(filename, "rb");
  283.     if (inFile == NULL) {
  284.     fprintf(stderr, "%s: cannot open %s: ", program, filename);
  285.     perror("");
  286.     return;
  287.     }
  288.  
  289.     /*
  290.      * pre-process prefix if needed
  291.      */
  292.     if (pre_str == NULL) {
  293.     if (i_flag) {
  294.         filename_len = strlen(filename);
  295.         MD5Update(dig, (BYTE *)filename, filename_len);
  296.         COUNT(dig, filename_len);
  297.     }
  298.     } else {
  299.     if (i_flag) {
  300.         MD5Update(dig, pre_str, pre_len);
  301.         filename_len = strlen(filename);
  302.         MD5Update(dig, (BYTE *)filename, filename_len);
  303.         COUNT(dig, pre_len + filename_len);
  304.     } else {
  305.         MD5Update(dig, pre_str, pre_len);
  306.         COUNT(dig, pre_len);
  307.     }
  308.     }
  309.  
  310.     /*
  311.      * digest file stat and lstat
  312.      */
  313.     if (i_flag) {
  314.     if (fstat(fileno(inFile), &buf) < 0) {
  315.         printf("%s can't be stated.\n", filename);
  316.         return;
  317.     }
  318.     hashbuf.st_dev = buf.st_dev;
  319.     hashbuf.st_ino = buf.st_ino;
  320.     hashbuf.st_mode = buf.st_mode;
  321.     hashbuf.st_nlink = buf.st_nlink;
  322.     hashbuf.st_uid = buf.st_uid;
  323.     hashbuf.st_gid = buf.st_gid;
  324.     hashbuf.st_size = buf.st_size;
  325.     hashbuf.st_mtime = buf.st_mtime;
  326.     hashbuf.st_ctime = buf.st_ctime;
  327.     MD5Update(dig, (BYTE *)&hashbuf, sizeof(hashbuf));
  328.     if (lstat(filename, &buf) < 0) {
  329.         printf("%s can't be lstated.\n", filename);
  330.         return;
  331.     }
  332.     hashlbuf.st_dev = buf.st_dev;
  333.     hashlbuf.st_ino = buf.st_ino;
  334.     hashlbuf.st_mode = buf.st_mode;
  335.     hashlbuf.st_nlink = buf.st_nlink;
  336.     hashlbuf.st_uid = buf.st_uid;
  337.     hashlbuf.st_gid = buf.st_gid;
  338.     hashlbuf.st_size = buf.st_size;
  339.     hashlbuf.st_mtime = buf.st_mtime;
  340.     hashlbuf.st_ctime = buf.st_ctime;
  341.     MD5Update(dig, (BYTE *)&hashlbuf, sizeof(hashlbuf));
  342.  
  343.     /*
  344.      * pad with zeros to process file data faster
  345.      */
  346.     if (dig->datalen > 0) {
  347.         COUNT(dig, sizeof(hashbuf) + sizeof(hashlbuf) +
  348.               MD5_CHUNKSIZE - dig->datalen);
  349.         MD5Update(dig, (BYTE *)zero, MD5_CHUNKSIZE - dig->datalen);
  350.     } else {
  351.         COUNT(dig, sizeof(hashbuf) + sizeof(hashlbuf));
  352.     }
  353.     }
  354.  
  355.     /*
  356.      * process the data stream
  357.      */
  358.     MD5Stream(NULL, 0, inFile, dig);
  359.     fclose(inFile);
  360. }
  361.  
  362.  
  363. /*
  364.  * MD5Output - output the digest
  365.  */
  366. static void
  367. MD5Output(str, quot, dig)
  368.     char *str;        /* print string after digest, NULL => none */
  369.     int quot;        /* 1 => surround str with a double quotes */
  370.     MD5_CTX *dig;    /* current digest */
  371. {
  372.     /*
  373.      * finalize the digest
  374.      */
  375.     MD5Final(dig);
  376.  
  377.     /*
  378.      * print the digest
  379.      */
  380.     MD5Print(dig);
  381.     if (str && !q_flag) {
  382.     if (quot) {
  383.         printf(" \"%s\"\n", str);
  384.     } else {
  385.         printf(" %s\n", str);
  386.     }
  387.     } else {
  388.     putchar('\n');
  389.     }
  390.     fflush(stdout);
  391. }
  392.  
  393.  
  394. /*
  395.  * MD5Print - print a digest in hex
  396.  *
  397.  * Prints message digest buffer in MD5Info as 40 hexadecimal digits. Order is
  398.  * from low-order byte to high-order byte of digest. Each byte is printed
  399.  * with high-order hexadecimal digit first.
  400.  *
  401.  * If -c, then print a leading "0x".  If -i, then print a trailing ".0".
  402.  */
  403. void
  404. MD5Print(MD5Info)
  405.     MD5_CTX *MD5Info;
  406. {
  407.     int i;
  408.  
  409.     if (c_flag) {
  410.     fputs("0x", stdout);
  411.     }
  412.     for (i = 0; i < 16; i++) {
  413.     printf ("%02x", MD5Info->digest[i]);
  414.     }
  415.     if (dot_zero) {
  416.     fputs(".0", stdout);
  417.     }
  418. }
  419.  
  420.  
  421. /*
  422.  * A time trial routine, to measure the speed of MD5.
  423.  *
  424.  * Measures user time required to digest TEST_MEG megabytes of characters.
  425.  */
  426. static void
  427. MD5TimeTrial()
  428. {
  429.     BYTE data[READSIZE];    /* test buffer */
  430.     MD5_CTX MD5Info;        /* hash state */
  431.     struct rusage start;    /* test start time */
  432.     struct rusage stop;        /* test end time */
  433.     double usrsec;        /* duration of test in user seconds */
  434.     unsigned int i;
  435.  
  436.     /* initialize test data */
  437.     for (i = 0; i < READSIZE; i++)
  438.     data[i] = (BYTE)(i & 0xFF);
  439.  
  440.     /* start timer */
  441.     if (!q_flag) {
  442.     printf("md5 time trial for %d megabytes of test data ...", TEST_MEG);
  443.     fflush(stdout);
  444.     }
  445.     getrusage(RUSAGE_SELF, &start);
  446.  
  447.     /* digest data in READSIZE byte chunk */
  448.     MD5Init(&MD5Info);
  449.     for (i=0; i < TEST_CHUNKS; ++i) {
  450.     MD5fullUpdate(&MD5Info, data, READSIZE);
  451.     }
  452.     COUNT(&MD5Info, READSIZE*TEST_CHUNKS);
  453.     MD5Final(&MD5Info);
  454.  
  455.     /* stop timer, get time difference */
  456.     getrusage(RUSAGE_SELF, &stop);
  457.     usrsec = (stop.ru_utime.tv_sec - start.ru_utime.tv_sec) +
  458.            (double)(stop.ru_utime.tv_usec - start.ru_utime.tv_usec)/1000000.0;
  459.     if (!q_flag) {
  460.     putchar('\n');
  461.     }
  462.     MD5Print(&MD5Info);
  463.     if (q_flag) {
  464.     putchar('\n');
  465.     } else {
  466.     printf(" is digest of test data\n");
  467.     printf("user seconds to process test data: %.2f\n", usrsec);
  468.     printf("characters processed per user second: %d\n",
  469.         (int)((double)TEST_MEG*1024.0*1024.0/usrsec));
  470.     }
  471. }
  472.  
  473.  
  474. /*
  475.  * Runs a standard suite of test data.
  476.  */
  477. static void
  478. MD5TestSuite()
  479. {
  480.     struct MD5_test *t;        /* current MD5 test */
  481.     struct stat buf;        /* stat of a test file */
  482.     MD5_CTX digest;        /* test digest */
  483.     char **f;            /* current file being tested */
  484.     int i;
  485.  
  486.     /*
  487.      * copy our test strings into writable data
  488.      */
  489.     for (i=0, t=MD5_test_data; i < MAX_MD5_TEST_DATA; ++i, ++t) {
  490.     if (t->ro_data != NULL) {
  491.         t->data = (BYTE *)malloc(t->len + 1);
  492.         if (t->data == NULL) {
  493.         fprintf(stderr, "%s: malloc #4 failed\n", program);
  494.         exit(4);
  495.         }
  496.         strcpy((char *)t->data, (char *)t->ro_data);
  497.         }
  498.     }
  499.  
  500.     /*
  501.      * print test header
  502.      */
  503.     puts("md5 test suite results:");
  504.  
  505.     /*
  506.      * find all of the test files
  507.      */
  508.     for (i=0, f=MD5_test_file; i < MAX_MD5_TEST_FILE; ++i, ++f) {
  509.     if (stat(*f, &buf) < 0) {
  510.         /* no file1 in this directory, cd to the test suite directory */
  511.         if (chdir(TLIB) < 0) {
  512.         fflush(stdout);
  513.         fprintf(stderr,
  514.             "%s: cannot find %s or %s/%s\n", program, *f, TLIB, *f);
  515.         return;
  516.         }
  517.     }
  518.     }
  519.  
  520.     /*
  521.      * try all combinations of test strings as prefixes and data
  522.      */
  523.     for (i=0, t=MD5_test_data; i < MAX_MD5_TEST_DATA; ++i, ++t) {
  524.     MD5Init(&digest);
  525.     MD5Update(&digest, t->data, t->len);
  526.     COUNT(&digest, t->len);
  527.     MD5Output((char *)(t->data), 1, &digest);
  528.     }
  529.  
  530.     /*
  531.      * try the files with all test strings as prefixes
  532.      */
  533.     for (i=0, f=MD5_test_file; i < MAX_MD5_TEST_FILE; ++i, ++f) {
  534.     MD5Init(&digest);
  535.     MD5File(NULL, 0, *f, &digest);
  536.     MD5Output(*f, 0, &digest);
  537.     }
  538.     exit(0);
  539. }
  540.  
  541.  
  542. /*
  543.  * MD5PreFileRead - read and process a prepend file
  544.  *
  545.  * Returns the length of pre_str, and modifies pre_str to
  546.  * point at the malloced prepend data.
  547.  */
  548. static int
  549. MD5PreFileRead(pre_file, buf)
  550.     char *pre_file;        /* form pre_str from file pre_file */
  551.     BYTE **buf;            /* pointer to pre_str pointer */
  552. {
  553.     struct stat statbuf;    /* stat for pre_file */
  554.     int pre_len;        /* length of pre_file to be used */
  555.     int bytes;            /* bytes read from pre_file */
  556.     FILE *pre;            /* pre_file descriptor */
  557.  
  558.     /* obtain the length that we will use */
  559.     if (stat(pre_file, &statbuf) < 0) {
  560.     fprintf(stderr, "%s: unpable to find prepend file %s\n",
  561.         program, pre_file);
  562.     exit(5);
  563.     }
  564.     pre_len = statbuf.st_size;
  565.     if (pre_len > MAX_PRE_FILE) {
  566.     /* don't use beyond MAX_PRE_FILE in size */
  567.     pre_len = MAX_PRE_FILE;
  568.     }
  569.  
  570.     /* malloc our pre string */
  571.     *buf = (BYTE *)malloc(pre_len+1);
  572.     if (*buf == NULL) {
  573.     fprintf(stderr, "%s: malloc #3 failed\n", program);
  574.     exit(6);
  575.     }
  576.  
  577.     /* open our pre_file */
  578.     pre = fopen(pre_file, "rb");
  579.     if (pre == NULL) {
  580.     fprintf(stderr, "%s: unable to open prepend file %s\n",
  581.       program, pre_file);
  582.     exit(7);
  583.     }
  584.  
  585.     /* read our pre_file data */
  586.     bytes = fread((char *)(*buf), 1, pre_len, pre);
  587.     if (bytes != pre_len) {
  588.     fprintf(stderr,
  589.       "%s: unable to read %d bytes from prepend file %s\n",
  590.       program, pre_len, pre_file);
  591.     exit(8);
  592.     }
  593.  
  594.     /* return our length */
  595.     return (pre_len);
  596. }
  597.  
  598.  
  599. /*
  600.  * MD5Help - print MD5 help message and exit
  601.  */
  602. static void
  603. MD5Help()
  604. {
  605.     fprintf(stderr,
  606.       "%s [-cdhiqtx][-p str][-P str][-s str] [file ...]\n",
  607.       program);
  608.     fprintf(stderr,
  609.       "    -c          print C style digests with a leading 0x\n");
  610.     fprintf(stderr,
  611.       "    -d          dual digests of even and odd indexed bytes\n");
  612.     fprintf(stderr,
  613.       "    -h          prints this message\n");
  614.     fprintf(stderr,
  615.       "    -i          process inode and filename as well as file data\n");
  616.     fprintf(stderr,
  617.       "    -p str      prepend str to data before digesting\n");
  618.     fprintf(stderr,
  619.       "    -P str      prepend the file 'str' to data before digesting\n");
  620.     fprintf(stderr,
  621.       "    -q          print only the digest\n");
  622.     fprintf(stderr,
  623.       "    -s str      prints digest and contents of string\n");
  624.     fprintf(stderr,
  625.       "    -t          prints time statistics for %dM chars\n", TEST_MEG);
  626.     fprintf(stderr,
  627.       "    -v          print version\n");
  628.     fprintf(stderr,
  629.       "    -x          execute an extended standard suite of test data\n");
  630.     fprintf(stderr,
  631.       "    file        print digest and name of file\n");
  632.     fprintf(stderr,
  633.       "    (no args)   print digest of stdin\n");
  634.     exit(0);
  635. }
  636.  
  637.  
  638. /*
  639.  * main - MD5 main
  640.  */
  641. void
  642. main(argc, argv)
  643.     int argc;            /* arg count */
  644.     char **argv;        /* the args */
  645. {
  646.     MD5_CTX digest;        /* our current digest */
  647.     BYTE *pre_str = NULL;    /* pre-process this data first */
  648.     char *pre_file = NULL;    /* pre-process this file first */
  649.     char *data_str = NULL;    /* data is this string, not a file */
  650.     UINT pre_str_len;        /* length of pre_str or pre_file */
  651.     UINT data_str_len;        /* length of data_str */
  652.     int d_flag = 0;        /* 1 => dual digest mode */
  653.     int t_flag = 0;        /* 1 => -t was given */
  654.     int x_flag = 0;        /* 1 => -x was given */
  655.     extern char *optarg;    /* argument to option */
  656.     extern int optind;        /* option index */
  657.     int c;
  658.  
  659.     /*
  660.      * parse args
  661.      */
  662.     program = argv[0];
  663.     while ((c = getopt(argc, argv, "cdihp:P:qs:tvx")) != -1) {
  664.         switch (c) {
  665.         case 'c':
  666.         c_flag = 1;
  667.         break;
  668.         case 'd':
  669.         d_flag = 1;
  670.         break;
  671.     case 'h':
  672.         MD5Help();
  673.         /*NOTREACHED*/
  674.         break;
  675.     case 'i':
  676.             i_flag = 1;
  677.             break;
  678.     case 'p':
  679.         pre_str = (BYTE *)optarg;
  680.         break;
  681.     case 'q':
  682.         q_flag = 1;
  683.         break;
  684.     case 'P':
  685.         pre_file = optarg;
  686.         break;
  687.         case 's':
  688.             data_str = optarg;
  689.             break;
  690.     case 't':
  691.         t_flag = 1;
  692.         break;
  693.     case 'v':
  694.         printf("%s: version 2.%s.%s%s %s\n",
  695.                 program, "%R%", "%L%",
  696.             (strcmp(MD5_what,"@(#)") == 0 &&
  697.              strcmp("%Z%","@(#)") == 0 &&
  698.              strcmp(MD5dual_what,"@(#)") == 0 &&
  699.              strcmp(MD5_H_WHAT,"@(#)") == 0) ? "" : "+",
  700.             "%D%");
  701.         exit(0);
  702.     case 'x':
  703.         x_flag = 1;
  704.         break;
  705.     default:
  706.         MD5Help();
  707.         break;
  708.         }
  709.     }
  710.     if (data_str && optind != argc) {
  711.     fprintf(stderr, "%s: -s is not compatible with digesting files\n",
  712.         program);
  713.     exit(9);
  714.     }
  715.     if (i_flag && optind == argc) {
  716.     fprintf(stderr, "%s: -i works only on filenames\n", program);
  717.     exit(10);
  718.     }
  719.  
  720.     /*
  721.      * process -x if needed
  722.      */
  723.     if (x_flag) {
  724.         if (d_flag) {
  725.             dualTest();
  726.     } else {
  727.         MD5TestSuite();
  728.     }
  729.     exit(0);
  730.     }
  731.  
  732.     /*
  733.      * process -t if needed
  734.      */
  735.     if (t_flag) {
  736.     MD5TimeTrial();
  737.     exit(0);
  738.     }
  739.  
  740.     /*
  741.      * process -P or -p if needed
  742.      */
  743.     if (pre_str && pre_file) {
  744.     fprintf(stderr, "%s: -p and -P conflict\n", program);
  745.     exit(11);
  746.     }
  747.     if (pre_file) {
  748.     pre_str_len = MD5PreFileRead(pre_file, &pre_str);
  749.     } else if (pre_str) {
  750.         pre_str_len = strlen((char *)pre_str);
  751.     } else {
  752.         pre_str_len = 0;
  753.     }
  754.  
  755.     /*
  756.      * if -d, perform dual digest processing instead
  757.      */
  758.     if (d_flag) {
  759.     dualMain(argc, argv, pre_str, pre_str_len, data_str);
  760.  
  761.     /*
  762.      * if no -d, process string, stdin or files
  763.      */
  764.     } else {
  765.  
  766.     /*
  767.      * case: digest a string
  768.      */
  769.     if (data_str != NULL) {
  770.         data_str_len = strlen(data_str);
  771.         MD5Init(&digest);
  772.         MD5Update(&digest, pre_str, pre_str_len);
  773.         MD5Update(&digest, (BYTE *)data_str, data_str_len);
  774.         COUNT(&digest, pre_str_len + data_str_len);
  775.         MD5Output(data_str, 1, &digest);
  776.  
  777.     /*
  778.      * case: digest stdin
  779.      */
  780.     } else if (optind == argc) {
  781.         MD5Init(&digest);
  782.         MD5Stream(pre_str, pre_str_len, stdin, &digest);
  783.         MD5Output(NULL, 0, &digest);
  784.  
  785.     /*
  786.      * case: digest files
  787.      */
  788.     } else {
  789.         if (i_flag) {
  790.         dot_zero = 1;
  791.         }
  792.         for (; optind < argc; optind++) {
  793.         MD5Init(&digest);
  794.         MD5File(pre_str, pre_str_len, argv[optind], &digest);
  795.         MD5Output(argv[optind], 0, &digest);
  796.         }
  797.     }
  798.     }
  799.  
  800.     /* all done */
  801.     exit(0);
  802. }
  803.